using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using haptic.src.engine;
using haptic.src.engine.render;
using haptic.src.engine.debug;
using haptic.src.engine.animation;
using haptic.src.components;
using haptic.src.engine.scene;
using haptic.src.engine.sprite;
using haptic.src.engine.maths;
using haptic.src.engine.resource;
using System.IO;
using haptic.src.engine.file;
using haptic.src.engine.collisions;

namespace haptic
{
    public class HapticGame : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager m_oGraphics;
        Renderer m_oRenderer;
        DebugRenderer m_oDebugRenderer;
        SpriteBatch m_oSpriteBatch;
        Scene m_oScene;
        Node m_oSceneGraph;

        public HapticGame()
        {
            m_oScene = new Scene();

            m_oGraphics = new GraphicsDeviceManager(this);
            m_oGraphics.PreferMultiSampling = true;
            m_oGraphics.SynchronizeWithVerticalRetrace = true;

            m_oRenderer = new Renderer(this);
            m_oDebugRenderer = new DebugRenderer(m_oRenderer, this);

            // Singletons
            ResourceManager.Create(Content);
           
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            base.Initialize();
        }

        protected override void LoadContent()
        {
            m_oSpriteBatch = new SpriteBatch(GraphicsDevice);
            m_oRenderer.m_oSpriteBatch = m_oSpriteBatch;
            m_oRenderer.m_oCameraManager = m_oScene.m_oCameraManager;
            m_oRenderer.Load();

            // temp
            Level oLevel = new Level(m_oScene, Services, "Content/Levels/test.level.xml");

            // Automate
            {
                Automate oAutomate = new Automate("Content/Characters/P1.automate.xml");
                oAutomate.m_sDefaultState = "idle";

                // idle
                {
                    State oState = new State("idle");
                    oState.m_fDuration = 0.0f;
                    StateLoop oStateLoop = new StateLoop();
                    oState.m_oController = oStateLoop;
                    haptic.src.engine.animation.Sprite oSprite = new haptic.src.engine.animation.Sprite("Characters/P1/Idle");
                    oSprite.m_uiFrameWidth = 96;
                    StateEffectSprite oSpriteEffect = new StateEffectSprite(oSprite);
                    oState.m_oEffects.Add(oSpriteEffect);
                    oAutomate.AddState(oState);
                }

                // fall
                {
                    State oState = new State("fall");
                    oState.m_fDuration = 0.0f;
                    StateStayOnLastFrame oStateCntrl = new StateStayOnLastFrame();
                    oState.m_oController = oStateCntrl;
                    haptic.src.engine.animation.Sprite oSprite = new haptic.src.engine.animation.Sprite("Characters/P1/Fall");
                    oSprite.m_uiFrameWidth = 96;
                    StateEffectSprite oSpriteEffect = new StateEffectSprite(oSprite);
                    oSpriteEffect.m_bAutoFlip = true;
                    oState.m_oEffects.Add(oSpriteEffect);
                    oAutomate.AddState(oState);
                }

                // jump
                {
                    State oState = new State("jump");
                    oState.m_fDuration = 0.2f;
                    StateStayOnLastFrame oStateCntrl = new StateStayOnLastFrame();
                    oState.m_oController = oStateCntrl;
                    haptic.src.engine.animation.Sprite oSprite = new haptic.src.engine.animation.Sprite("Characters/P1/Jump");
                    oSprite.m_uiFrameWidth = 96;
                    StateEffectSprite oSpriteEffect = new StateEffectSprite(oSprite);
                    oSpriteEffect.m_bAutoFlip = true;
                    oSpriteEffect.m_bFlipHorizontally = true;
                    oState.m_oEffects.Add(oSpriteEffect);
                    oAutomate.AddState(oState);
                }

                // run
                {
                    State oState = new State("run");
                    oState.m_fDuration = 0.6f;
                    StateLoop oStateLoop = new StateLoop();
                    oState.m_oController = oStateLoop;
                    haptic.src.engine.animation.Sprite oSprite = new haptic.src.engine.animation.Sprite("Characters/P1/Run");
                    oSprite.m_uiFrameWidth = 96;
                    StateEffectSprite oSpriteEffect = new StateEffectSprite(oSprite);
                    oSpriteEffect.m_bAutoFlip = true;
                    oSpriteEffect.m_bFlipHorizontally = true;
                    oState.m_oEffects.Add(oSpriteEffect);
                    oAutomate.AddState(oState);
                }
                
                oAutomate.Serialize();
            }

            // Player
            {
                Entity oEntity = new Entity();
                oEntity.m_vSize = new Vector2(40.0f, 96.0f);
                oEntity.m_vPosition = new Vector2(70, 0);
                cDynamic oDynamic = new cDynamic(oEntity);
                cAppearance oAppearance = new cAppearance(oEntity);
                cPhysics oPhysics = new cPhysics(oEntity);
                cPlayer oPlayer = new cPlayer(oEntity);
                cCollider oCollider = new cCollider(oEntity);
                cAutomate oAutomate = new cAutomate(oEntity);
                haptic.src.engine.animation.Sprite oSprite = new haptic.src.engine.animation.Sprite("Characters/P1/Run");
                //Sprite oSprite = new Sprite("Characters/P1/RunSmooth3");
                oSprite.fTotalDuration = 1.0f;
                oSprite.m_uiFrameWidth = 96;
                oSprite.m_uiFrameHeight = 96;
                oAppearance.m_oSprite = oSprite;
                oAutomate.m_sFilePath = "Content/Characters/P1.automate.xml";
                //oAppearance.m_sTexturePath = "Characters/Idle";
                oEntity.AddComponent(oPlayer);
                oEntity.AddComponent(oDynamic);
                oEntity.AddComponent(oPhysics);
                oEntity.AddComponent(oCollider);
                //oEntity.AddComponent(oAppearance);
                oEntity.AddComponent(oAutomate);
                oLevel.Add(oEntity);
            }
            
            // Ground
            for (float x = 0.0f; x < 800.0f; x+= 64.0f )
            {
                Entity oEntity = new Entity();
                if (x < 300.0f)
                    oEntity.m_vPosition = new Vector2(x, 400.0f);
                else
                    oEntity.m_vPosition = new Vector2(x, 400.0f + ((x-300.0f)*(x - 300.0f))*0.001f);
                cAppearance oAppearance = new cAppearance(oEntity);
                cCollider oCollider = new cCollider(oEntity);
                oAppearance.m_sTexturePath = "Obstacles/block";
                oEntity.AddComponent(oAppearance);
                oEntity.AddComponent(oCollider);
                oLevel.Add(oEntity);
            }
            for (float x = 800.0f; x < 1600.0f; x += 64.0f)
            {
                Entity oEntity = new Entity();
                oEntity.m_vPosition = new Vector2(x, 600);
                cAppearance oAppearance = new cAppearance(oEntity);
                cCollider oCollider = new cCollider(oEntity);
                oAppearance.m_sTexturePath = "Obstacles/block";
                oEntity.AddComponent(oAppearance);
                oEntity.AddComponent(oCollider);
                oLevel.Add(oEntity);
            }
            {
                Entity oEntity = new Entity();
                oEntity.m_vPosition = new Vector2(250, 200);
                cAppearance oAppearance = new cAppearance(oEntity);
                cCollider oCollider = new cCollider(oEntity);
                oAppearance.m_sTexturePath = "Obstacles/block";
                oEntity.AddComponent(oAppearance);
                oEntity.AddComponent(oCollider);
                oLevel.Add(oEntity);
            }
            {
                Entity oEntity = new Entity();
                oEntity.m_vPosition = new Vector2(180, 250);
                cSceneGraph oSceneGraph = new cSceneGraph(oEntity);
                oSceneGraph.m_oNode = new RootNode();
                oSceneGraph.m_oNode.Link(new SpriteNode(new haptic.src.engine.sprite.Sprite("test.sprite")));
                AABBCollider oCollider = new AABBCollider(Vector2.Zero, new Vector2(20, 20));
                CollisionNode oColNode = new CollisionNode(oCollider);
                oSceneGraph.m_oNode.Link(oColNode);
                oEntity.AddComponent(oSceneGraph);
                oLevel.Add(oEntity);
            }

            // Background
            {
                Entity oEntity = new Entity();
                oEntity.m_iLayer = 2000;
                oEntity.m_vPosition = new Vector2(400, 300);
                cAppearance oAppearance = new cAppearance(oEntity);
                cAmbientSound oAmbientSound = new cAmbientSound(oEntity);
                oAmbientSound.m_sSoundPath = @"Sounds/Music/06 - 23 hanasi";
                oAppearance.m_sTexturePath = "Background/background";
                oEntity.AddComponent(oAppearance);
                //oEntity.AddComponent(oAmbientSound);
                oLevel.Add(oEntity);
            }

            // Camera
            {
                Entity oEntity = new Entity();
                oEntity.m_vPosition = new Vector2(400, 300);
                cGameCamera oCamera = new cGameCamera(oEntity);
                oEntity.AddComponent(oCamera);
                oLevel.Add(oEntity);
            }

            // SceneGraph
            {
                //ResourceManager.Inst.Get("d:/test.sprite.xml");
                SpriteData oSpriteData = new SpriteData("Obstacles/block");
                oSpriteData.m_sTexturePath = "Obstacles/block";
                oSpriteData.Save("test.sprite");
                SpriteNode oSpriteNode = new SpriteNode(new haptic.src.engine.sprite.Sprite("test.sprite"));
                Node oChild = new Node();
                SpriteNode oSpriteNode2 = new SpriteNode(new haptic.src.engine.sprite.Sprite("test.sprite"));
                oChild.Link(oSpriteNode2);
                oSpriteNode2.mLocal = Mat2x3.CreateTranslation(new Vector2(50, 20));
                m_oSceneGraph = new RootNode();
                m_oSceneGraph.mLocal = Mat2x3.CreateTranslation(new Vector2(100, 100));
                m_oSceneGraph.Link(oSpriteNode);
                m_oSceneGraph.Link(oChild);
                m_oSceneGraph.Load();
            }

            // Animation
            {
                AnimationData oAnimData = new AnimationData();
                Animation oAnim = new Animation(oAnimData);
            }

            LevelIO.Serialize(oLevel);

            m_oScene.m_oCurrentLevel = oLevel;

            m_oScene.Load();
        }

        protected override void UnloadContent()
        {
        }

        protected override void Update(GameTime oGameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();
            KeyboardState oKbState = Keyboard.GetState();

            if (oKbState.IsKeyDown(Keys.Escape))
                this.Exit();

            if ((oKbState.IsKeyDown(Keys.LeftAlt) || oKbState.IsKeyDown(Keys.RightAlt)) && oKbState.IsKeyDown(Keys.Enter))
                m_oGraphics.ToggleFullScreen();

            m_oScene.Update(oGameTime);

            m_oSceneGraph.Update(oGameTime);

            base.Update(oGameTime);
        }

        protected override void Draw(GameTime oGameTime)
        {
            m_oRenderer.Resize(m_oGraphics.GraphicsDevice.Viewport.Width, m_oGraphics.GraphicsDevice.Viewport.Height);
            GraphicsDevice.Clear(Color.CornflowerBlue);

            m_oScene.Draw(m_oRenderer, m_oDebugRenderer, oGameTime);

            m_oSpriteBatch.Begin();
            m_oSceneGraph.Draw(m_oRenderer);
            m_oSpriteBatch.End();

            base.Draw(oGameTime);
        }
    }
}
